home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
722
/
722.xpi
/
chrome
/
noscript.jar
/
content
/
noscript
/
DNS.js
< prev
next >
Wrap
Text File
|
2010-02-12
|
10KB
|
353 lines
function DNSRecord(record) {
this.ts = Date.now();
var ttl;
if (record) {
try {
this.canonicalName = record.canonicalName;
} catch(e) {}
this.entries = [];
try {
for (;;) this.entries.push(record.getNextAddrAsString());
} catch(e) {
// NS_ERROR_NOT_AVAILABLE, no more records
}
ttl = this.TTL;
if (!this.entries.length) this.valid = false;
} else {
this.valid = false;
}
if (!this.valid) ttl = Thread.canSpin ? this.INVALID_TTL_ASYNC : this.INVALID_TTL_SYNC;
this.expireTime = this.ts + ttl;
}
DNSRecord.prototype = {
INVALID_TTL_ASYNC: 3000,
INVALID_TTL_SYNC: 8000,
TTL: 60000,
valid: true,
ts: 0,
entries: [],
canonicalName: '',
expireTime: 0,
refreshing: false,
isLocal: function(all) {
return all
? "everyLocal" in this
? this.everyLocal
: this.everyLocal = this.entries.every(DNS.isLocalIP, DNS)
: "someLocal" in this
? this.someLocal
: this.someLocal = this.entries.some(DNS.isLocalIP, DNS)
;
},
get expired() {
return Date.now() > this.expireTime;
}
}
var DNS = {
get logFile() {
delete this.logFile;
var logFile = CC["@mozilla.org/file/directory_service;1"]
.getService(CI.nsIProperties).get("ProfD", CI.nsIFile);
logFile.append("noscript_dns.log");
return this.logFile = logFile;
},
logEnabled: false,
log: function(msg) {
try {
if (!this.logStream) {
const logFile = this.logFile;
const logStream = CC["@mozilla.org/network/file-output-stream;1"]
.createInstance(CI.nsIFileOutputStream);
logStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0 );
this.logStream = logStream;
const header="*** Log start at "+new Date().toGMTString()+"\n";
this.logStream.write(header,header.length);
}
if (msg!=null) {
msg += "\n";
this.logStream.write(msg,msg.length);
}
this.logStream.flush();
} catch(ex) {
dump(ex.message+"\noccurred logging this message:\n"+msg);
}
},
get _dns() {
delete this._dns;
return this._dns = CC["@mozilla.org/network/dns-service;1"]
.getService(CI.nsIDNSService);
},
_cache: {
CAPACITY: 400, // when we purge, we cut this to half
_map: {},
_ext: {},
get count() {
return this._map.__count__;
},
get: function(key) {
return key in this._map && this._map[key];
},
put: function(key, entry) {
if (!(key in this._map)) {
if (this.count >= this.CAPACITY) {
this.purge();
}
}
this._map[key] = entry;
},
evict: function(host) {
return (host in this._map) && (delete this._map[host]);
},
purge: function() {
var max = this.CAPACITY / 2;
if (this.count < max) return;
var l = [];
var map = this._map;
for (var key in map) {
l.push({ k: key, t: map[key].ts});
}
this._doPurge(map, l, max);
},
_oldLast: function(a, b) {
return a.t > b.t ? -1 : a.t < b.t ? 1 : 0;
},
putExt: function(host) {
this._ext[host] = true; // Date.now();
// we prefer to store a few bytes indefinitely rather than fall for DNS rebinding...
// if (this._ext.__count__ > 800) this._purgeExtCache();
},
isExt: function(host) {
return host in this._ext;
},
_purgeExtCache: function() {
var l = [];
var map = this._extCache;
for (var key in map) {
l.push({ k: key, t: map[key]});
}
this._doPurge(map, l, l.length / 2);
},
_doPurge: function(map, l, max) {
l.sort(this._oldLast);
for (var j = l.length; j-- > max;) {
delete map[l[j].k];
}
}
},
get _idn() {
delete this._idn;
return this._idn = CC["@mozilla.org/network/idn-service;1"]
.getService(CI.nsIIDNService);
},
_invalidRx: /[^\w\-\.]/,
checkHostName: function(host) {
if (this._invalidRx.test(host) && !this.isIP(host)) {
try {
host = this._idn.convertUTF8toACE(host);
} catch(e) {
return false;
}
return !this._invalidRx.test(host);
}
return true;
},
_resolving: {},
resolve: function(host, flags, callback) {
flags = flags || 0;
var elapsed = 0, t;
var cache = this._cache;
var async = IOUtil.asyncNetworking && Thread.canSpin || !!callback;
var dnsRecord = cache.get(host);
if (dnsRecord) {
// cache invalidation, if needed
if (dnsRecord.expired && !dnsRecord.refreshing) {
if (dnsRecord.valid && !(flags & 1)) {
// refresh async
dnsRecord.refreshing = true;
DNS._dns.asyncResolve(host, flags, new DNSListener(function() {
if (DNS.logEnabled) DNS.log("Async " + host);
cache.put(host, dnsRecord = new DNSRecord(this.record));
}), Thread.currentQueue);
} else {
flags |= 1;
}
if (flags & 1) {
dnsRecord = null;
cache.evict(host);
}
}
}
if (dnsRecord) {
if (ABE.consoleDump) ABE.log("Using cached DNS record for " + host);
} else if (this.checkHostName(host)) {
if (async) {
var resolving = this._resolving;
if (host in resolving) {
ABE.log("Already resolving " + host);
if (callback) {
resolving[host].push(callback);
return null;
}
} else resolving[host] = callback ? [callback] : [];
var ctrl = {
running: true,
startTime: Date.now()
};
var status = Components.results.NS_OK;
var resolve = function() {
DNS._dns.asyncResolve(host, flags, new DNSListener(function() {
if (DNS.logEnabled) DNS.log("Async " + host);
cache.put(host, dnsRecord = new DNSRecord(this.record));
ctrl.running = false;
var callbacks = resolving[host];
delete resolving[host];
if (ABE.consoleDump && t) {
elapsed = Date.now() - t;
ABE.log("Async DNS query on " + host + " done, " + elapsed + "ms, callbacks: " + (callbacks && callbacks.length));
}
if (callbacks && callbacks.length)
for each(var cb in callbacks)
cb(dnsRecord);
}), Thread.currentQueue);
if (ABE.consoleDump) ABE.log("Waiting for DNS query on " + host);
if (!callback) Thread.spin(ctrl);
}
if (callback) {
t = Date.now();
resolve();
return null;
}
Thread.runWithQueue(resolve);
if (!Components.isSuccessCode(status)) throw status;
elapsed = ctrl.elapsed || 0;
} else {
t = Date.now();
if (ABE.consoleDump) ABE.log("Performing DNS query on " + host);
if (DNS.logEnabled) DNS.log("Sync " + host);
cache.put(host, dnsRecord = new DNSRecord(this._dns.resolve(host, flags)));
elapsed = Date.now() - t;
}
} else {
this._cache.put(host, dnsRecord = new DNSRecord(null)); // invalid host name
}
if (ABE.consoleDump) ABE.log("DNS query on " + host + " done, " + elapsed + "ms");
if (callback) {
callback(dnsRecord);
} else {
if (!(dnsRecord && dnsRecord.valid)) throw Components.results.NS_ERROR_UNKNOWN_HOST;
}
return dnsRecord;
},
evict: function(host) {
ABE.log("Removing DNS cache record for " + host);
return this._cache.evict(host);
},
invalidate: function(host) {
var dnsRecord = this._cache.get(host);
if (!dnsRecord.valid) return false;
dnsRecord.valid = false;
dnsRecord.expireTime = 0;
return true;
},
getCached: function(host) {
return this._cache.get(host);
},
isCached: function(host) {
var res = this._cache.get(host);
return res && (res.valid || !res.expired);
},
isLocalURI: function(uri, all) {
var host;
try {
host = uri.host;
} catch(e) {
return false;
}
if (!host) return true; // local file:///
return this.isLocalHost(host, all);
},
isLocalHost: function(host, all) {
if (host == "localhost") return true;
if (this.isIP(host)) {
return this.isLocalIP(host);
}
if (all && this._cache.isExt(host)) return false;
var res = this.resolve(host, 0).isLocal(all);
if (!res) {
this._cache.putExt(host);
}
return res;
},
isLocalIP: function(addr) {
// see https://bug354493.bugzilla.mozilla.org/attachment.cgi?id=329492 for a more verbose but incomplete (missing IPV6 ULA) implementation
// Relevant RFCs linked at http://en.wikipedia.org/wiki/Private_network
return /^(?:(?:0|127|10|169\.254|172\.(?:1[6-9]|2\d|3[0-1])|192\.168)\..*\.[^0]\d*$|(?:(?:255\.){3}255|::1?)$|F(?:[CDF][0-9A-F]|E[89AB])[0-9A-F:]+::)/i.test(addr);
},
isIP: function(host) {
return /^(?:\d+\.){3}\d+$|:.*:/.test(host);
}
};
function DNSListener(callback) {
if (callback) this.callback = callback;
};
DNSListener.prototype = {
QueryInterface: xpcom_generateQI([CI.nsIDNSListener, CI.nsISupports]),
record: null,
status: 0,
callback: null,
onLookupComplete: function(req, rec, status) {
this.record = rec;
this.status = status;
if (this.callback) this.callback();
}
};